<?php
/**
 * User: @lose
 * Date: 2020-04-01
 * Time: 17:28
 */

namespace Frieza\Modules\AbTest\Controllers;

use App\Http\Controllers\Controller;
use App\Pockets\GridDataModel as pGridDataModel;
use Doraemon\service\ABTest\GetTestStrategy as sGetTestStrategy;
use Encore\Admin\Facades\Admin;
use Encore\Admin\Layout\Content;
use Encore\Admin\Grid;
use Encore\Admin\Widgets\Form;
use Encore\Admin\Widgets\Tab;
use Illuminate\Http\Request;
use Illuminate\Support\MessageBag;
use Doraemon\service\ABTest\AdminAbTest as sAdminAbTest;
use Doraemon\model\AbTest\AbTestBasicInfo as mAbTestBasicInfo;
use Doraemon\model\AbTest\AbTestGroupInfo as mAbTestGroupInfo;
use Frieza\Modules\AbTest\Actions\Release;
use Frieza\Modules\AbTest\Actions\ResetCache;
use Frieza\Modules\AbTest\Actions\Delete;
use Encore\Admin\Grid\Displayers\DropdownActions;
use App\Exceptions\InputErrorException;
use Doraemon\cache\AbTest\TestInfo as cTestInfo;
use Doraemon\cache\AbTest\GroupInfo as cGroupInfo;
use Doraemon\tools\Msg2Admin;
use Doraemon\service\DingTalkAlert;

class IndexController extends Controller
{
    public function index(Content $content, Request $request)
    {
        $where = $this->buildWhere($request->all());
        $page = $request->input('page', 1);
        $pageSize = $request->input('per_page', 15);
        $limit = [($page - 1) * $pageSize, $pageSize];
        $message = '！！！实验周期不要超过 30 天。（开始时间以「数据回收开始时间」为准）';
        $title = '提示';
        $html = Msg2Admin::html($title, $message, 'error');
        $list = sAdminAbTest::getList($where, $limit);
        return $content
                    ->body($html)
                    ->header('别哔哔，上个实验！')
                    ->breadcrumb(['text' => 'AB实验管理'])
                    ->row($this->_formatGrid($list));
    }

    public function create(Content $content, Request $request, $testId = 0)
    {
        $isSelect = true;
        if($testId == 0 || $request->is('admin/*/edit')) {
            $isSelect = false;
        }

        $tab = new Tab();
        $tab->add('实验信息', $this->_formBasicInfo($testId, $isSelect), !$testId ? true : false);
        if($testId) {
            $tab->add('分组信息', $this->_formGroupInfo($testId, $isSelect), $testId ? true : false);
        }

        return $content
                    ->header('别哔哔，上个实验！')
                    ->breadcrumb(['text' => 'AB实验管理', 'url' => 'abTest'], ['text' => '实验管理'])
                    ->row($tab);
    }

    public function _formatBasicExpend($expend, $total)
    {
        $gridModel = new pGridDataModel($expend, $total);

        $grid = Admin::grid($gridModel,
            function (Grid $grid) {

                $grid->disableExport();
                $grid->disableFilter();
                $grid->disableCreateButton();
                $grid->disablePagination();
                $grid->disableActions();
                $grid->disableRowSelector();
                $grid->disableColumnSelector();
                $grid->disableTools();

                $grid->column('title', '分组名称');
                $grid->column('group_unique_mark', '分组标识');
                $grid->column('group_desc', '分组描述');
                $grid->column('white_list', '白名单');
                $grid->column('flow_ratio', '流量占比')->display(function () {
                    return $this->flow_ratio . '%';
                });
                $grid->column('group_status', '分组状态')->display(function () {
                    return mAbTestGroupInfo::STATUS_DESC[$this->group_status];
                })->label([
                    mAbTestGroupInfo::NORMAL_STATUS => 'primary',
                    mAbTestGroupInfo::RELEASE_STATUS => 'success',
                    mAbTestGroupInfo::CLOSE_STATUS => 'default',
                ]);

            }
        );
        return $grid->render();
    }

    public function _formatGrid($list)
    {
        foreach ($list['list'] as &$info) {
            $info['id'] = $info['test_id'];
            if($info['expend']) {
                $info['expend'] = $this->_formatBasicExpend($info['expend'], $list['total']);
            }
        }
        $gridModel = new pGridDataModel($list['list'], $list['total']);

        $grid = Admin::grid($gridModel,
            function (Grid $grid) {
                $grid->paginate(15);
                $grid->setActionClass(DropdownActions::class);
                $grid->filter(function (Grid\Filter $filter) {
                    $filter->disableIdFilter();
                    $filter->column(1 / 2, function (Grid\Filter $filter) {
                        $filter->equal('title', '标题')->placeholder('输入标题关键字');
                        $filter->equal('department', '所属部门')->select(mAbTestBasicInfo::DEPARTMENT_DESC);
                        $filter->equal('start_time', '开始时间')->datetime();
                        $filter->equal('admin_id', '创建者')->select(Admin::user()->pluck('name', 'id')->toarray());
                    });
                    $filter->column(1 / 2, function (Grid\Filter $filter) {
                        $filter->equal('test_channel', '实验端')->select(mAbTestBasicInfo::CHANNEL_DESC);
                        $filter->equal('test_mark', '分流标识')->select(mAbTestBasicInfo::MARK_DESC);
                        $filter->equal('test_status', '实验状态')->select(mAbTestBasicInfo::BUSINESS_STATUS_DESC);
                    });
                });

                $grid->disableExport();
                $grid->disableRowSelector();

                $grid->column('test_id', '实验ID')->expand(function () {
                    return $this->expend;
                });
                $grid->column('title', '实验名称');
                $grid->column('test_unique_mark', '实验标识');
                $grid->column('department', '所属部门')->display(function () {
                    return mAbTestBasicInfo::DEPARTMENT_DESC[$this->department];
                });
                $grid->column('start_time', '数据回收开始时间');
                $grid->column('addtime', '创建时间');
                $grid->column('test_channel', '实验端')->display(function () {
                    return mAbTestBasicInfo::CHANNEL_DESC[$this->test_channel];
                });
                $grid->column('test_mark', '分流标识')->display(function () {
                    return mAbTestBasicInfo::MARK_DESC[$this->test_mark];
                });
                $grid->column('user_profile', '分群信息')->userProfile();
                $grid->column('test_status', '实验状态')->display(function () {
                    if ($this->test_status == mAbTestBasicInfo::NORMAL_STATUS) {
                        return $this->start_time < date('Y-m-d H:i:s') ? '进行中' : '未开始';
                    } else {
                        return mAbTestBasicInfo::STATUS_DESC[$this->test_status];
                    }
                });

                $grid->column('result_url', '查看结果')->display(function () {
                    if($this->result_url) {
                        return "<a href='{$this->result_url}' target='_blank' >查看结果</a>";
                    }
                });
                $grid->column('admin_id', '创建者')->display(function () {
                    return Admin::user()->find($this->admin_id)->name;
                });

                $grid->actions(function (Grid\Displayers\Actions $actions) {
                    $actions->disableDelete();
                    if($actions->row->start_time > date('Y-m-d H:i:s')) {
                        $actions->add(new Delete());
                    }

                    if ($actions->row->start_time < date('Y-m-d H:i:s')) {
                        $actions->disableDelete();
                        if($actions->row->test_status != mAbTestBasicInfo::RELEASE_STATUS) {
                            $actions->add(new Release);

                            if($actions->row->start_time < date('Y-m-d H:i:s')) {
                                $actions->add(new ResetCache);
                            }
                        }
                    }

                    if($this->row->test_status == mAbTestBasicInfo::RELEASE_STATUS) {
                        $actions->disableEdit();
                    }
                });

                $grid->footer(function () {
                    $html = "<a href=".'/admin/abTest/getStrategyForm'." target=".'_blank'.">&nbsp;</a>";
                    return $html;
                });
            }
        );
        return $grid;
    }

    public function _formBasicInfo($testId, $isSelect)
    {
        $info = [];
        if($testId) {
            $info = sAdminAbTest::getBasicInfo($testId);
        }

        $form = new Form($info);
        $form->fieldset('基础信息', function (Form $form) use ($testId){
            $form->hidden('test_id' ,'实验id');
            $form->text('title', '实验名称')->placeholder('请输入实验名称，不超过50个字')->required();
            if($testId) {
                $form->text('test_unique_mark', '实验标识')->placeholder('请输入实验标识，不支持中文')->required()
                    ->help('使用需求的英文名加版本号命名比如:demand_name_4_9_0;禁止使用中文;禁止使用数字开头')->readonly();
            } else {
                $form->text('test_unique_mark', '实验标识')->placeholder('请输入实验标识，仅支持数字字母下划线')->required()
                    ->help('使用需求的英文名加版本号命名比如:demand_name_4_9_0;禁止使用中文;禁止使用数字开头')->pattern('^[0-9a-zA-Z_]{1,}$');
            }

            $form->text('test_desc', '实验概述')->placeholder('请简单介绍实验，可以直接粘贴需求文档哦~')->required();
            $form->select('department', '所属部门')->options(mAbTestBasicInfo::DEPARTMENT_DESC)->default(mAbTestBasicInfo::GROWTH_DEPARTMENT)->required();

            if ($testId) {
                $form->datetime('start_time','数据回收开始时间')->required()->readonly()->help("<span  style=\"color: red; \">研发同学以此时间为准，开始统计线上数据。建议根据实验上线时间来填写，一旦填写后不可修改，请谨慎填写</span>");
            } else {
                $form->datetime('start_time','数据回收开始时间')->required()->help("<span  style=\"color: red; \">研发同学以此时间为准，开始统计线上数据。建议根据实验上线时间来填写，一旦填写后不可修改，请谨慎填写</span>");
            }
            $form->radio('test_channel', '实验端')->options(mAbTestBasicInfo::CHANNEL_DESC)->default(mAbTestBasicInfo::CLIENT_CHANNEL)->required();
            $form->radio('test_mark', '分流标识')->options(mAbTestBasicInfo::MARK_DESC)->default(mAbTestBasicInfo::UID_MARK)->required();
            $form->text('relevant_people', '实验相关人员')->placeholder('输入实验相关人员的名字（拼音），多个名字用英文（,）隔开')
                ->help('关闭实验时将发送通知给实验相关人员');
            $form->radio('show_graphs', '数据看板')->options([0 => '关闭', 1 => '开启'])->default(1)->required()->help('无需观测数据的实验（比如研发优化某服务性能后基于ab实验放量的场景），选择关闭即可。');
        });
        $form->fieldset('分群区', function (Form $form) {
            $form->html('1、没有被操作的设置项，默认不做区分，全部参与实验；2、不同设置项之间是并集关系');
            $form->userProfile('user_profile', '用户分群')->generate();
        });
        $form->action('/admin/abTest/saveBasicInfo');

        $form->disableReset();
        if($isSelect) {
            $form->disableSubmit();
        }

        return $form;
    }

    public function _formGroupInfo($testId, $isSelect)
    {
        // 从闪存中获取数据添加失败后的回显信息
        $information = session('_old_input');
        $groupList = sAdminAbTest::getGroupList($testId);
        $groupList = $information ? : $groupList;
        $form = new Form($groupList);
        $form->hidden('test_id', '实验id');
        $form->fieldGroup('group_list', '实验组配置区域', function ($form) {
            $form->hidden('group_id', '分组id');
            $form->text('title', '分组名称')->required();
            $form->hidden('group_unique_mark', '分组标识')->required()->help('使用自然数命名比如：分组1是 1 ;分组2是 2');
            $form->text('group_desc', '分组描述')->required();
            $form->select('flow_ratio', '分组流量占比')->options(array_map(function ($num) {
                return $num . '%';
            }, range(0,100)))->required();
            $form->textarea('white_list', '实验白名单')->placeholder('1、注意区分DID和UID；2、不同的id之间用英文逗号（,）隔开');
        });
        $form->disableReset();
        $form->action('/admin/abTest/saveGroupInfo');
        if($isSelect) {
            $form->disableSubmit();
        }

        return $form;
    }

    public function saveBasicInfo(Request $request)
    {
            $testId = $request->input('test_id', 0);
            $data = [
                'title' => $request->input('title', ''),
                'test_unique_mark' => $request->input('test_unique_mark', ''),
                'test_desc' => $request->input('test_desc', ''),
                'department' => $request->input('department', 1),
                'start_time' => $request->input('start_time', ''),
                'test_channel' => $request->input('test_channel', 1),
                'test_mark' => $request->input('test_mark', 1),
                'relevant_people' => $request->input('relevant_people', ''),
                'show_graphs' => $request->input('show_graphs', 1),
                'edittime' => date('Y-m-d H:i:s'),
                'test_status' => mAbTestBasicInfo::NORMAL_STATUS,
            ];
            // 如果id=0的话，默认给一个时间，如果不等于0的话，查看关键字段（）有没有调整，如果调整过得话，将时间调整为本次修改的时间
            if($testId == 0) {
                $data['admin_id'] = Admin::user()->id;
                $data['last_edit_time'] = date('Y-m-d H:i:s', strtotime(date('Y-m-d H:i:s')) + (90 * 86400));

                if (strtotime($data['start_time']) < time()) {
                    throw new InputErrorException('数据回收时间应大于创建时间', InputErrorException::DEFAULT_CODE, 'start_time');
                }
            }

            // 验证实验唯一标识
            $uniqueMark = mAbTestBasicInfo::get('*', ['test_unique_mark' => $data['test_unique_mark'], 'test_id[!]' => $testId]);
            if($uniqueMark) {
                throw new InputErrorException('实验标识已存在', InputErrorException::DEFAULT_CODE, 'test_unique_mark');
            }

            $testId = sAdminAbTest::saveBasicInfo($testId, $data, $request->input('user_profile'));
            if($testId) {
                return redirect(url("/admin/abTest/{$testId}/edit"));
            } else {
                $error = new MessageBag([
                    'title' => '保存失败，请重试'
                ]);
                return back()->with(compact('error'))->withInput();
            }
    }

    public function saveGroupInfo(Request $request)
    {
        try {
            $testId = $request->input('test_id', 0);
            if(!$testId) {
                throw new \Exception('实验ID不存在');
            }

            $groupList = $request->input('group_list', []);
            if(!$groupList) {
                throw new \Exception('至少配置一个实验');
            }

            $groupUniqueMark = max(array_column($groupList, 'group_unique_mark'));
            $groupUniqueMark = $groupUniqueMark != '' ? $groupUniqueMark : 0;
            // 自动生成实验标识
            foreach ($groupList as $key => $value) {
                if($value['group_unique_mark'] == '') {
                    $groupUniqueMark++;
                    $groupList[$key]['group_unique_mark'] = $groupUniqueMark;
                }
            }

            $flowRatioSum = array_sum(array_column(array_filter($groupList, function ($value) {
                return $value['_remove_'] == 0;
            }), 'flow_ratio'));

            if($flowRatioSum != 100) {
                throw new \Exception('你配置的实验分组流量之和不等于100%，请检查修改后重新提交保存。');
            }

            if(count(array_column($groupList, 'group_unique_mark')) != count(array_unique(array_column($groupList, 'group_unique_mark')))) {
                throw new \Exception('分组标识重复，请检查修改后重新提交保存。');
            }

            sAdminAbTest::saveGroupInfo($testId, $groupList, Admin::user()->id);
            $success = new MessageBag([
                'title' => '保存成功'
            ]);
            return back()->with(compact('success'));
        } catch (\Exception $e) {
            $error = new MessageBag([
                'title' => $e->getMessage()
            ]);
            return back()->with(compact('error'))->withInput();
        }
    }

    public function release(Request $request)
    {
        $basicInfo = [
            'test_id' => $request->input('test_id'),
            'result_url' => $request->input('result_url'),
        ];

        $groupInfo = [
            'group_id' => $request->input('group_id'),
        ];

        try {
            sAdminAbTest::release($basicInfo, $groupInfo);

            // 重置缓存
            $testInfo = mAbTestBasicInfo::getInfoByTestId($basicInfo['test_id']);
            cTestInfo::setInfo($testInfo['test_unique_mark'], $testInfo);

            $this->sendMsg($basicInfo['test_id'], $groupInfo['group_id']);
            $type = 'success';
            $content = '发布成功';
        } catch (\Exception $e) {
            $type = 'error';
            $content = $e->getMessage();
        }
        $result = [
            'status' => true,
            'then' => [
                'action' => 'refresh',
                'value'  => true,
            ],
            'toastr' => [
                'type' => $type,
                'content' => $content,
                'options' => [
                    'positionClass' => 'toast-top-center'
                ]
            ]
        ];

        return response()->json($result);
    }

    public static function deleteInfo(Request $request)
    {
        $testId = $request->get('_key');
        try {
            sAdminAbTest::deleteInfo($testId);
            return response()->json([
                'status' => true,
                'message' => "删除成功",
            ]);
        } catch (\Exception $e) {
            return response()->json([
                'status' => false,
                'message' => $e->getMessage(),
            ]);
        }
    }

    private function buildWhere($params)
    {
        $where = [];
        $where['test_status[!]'] = mAbTestBasicInfo::DELETE_STATUS;
        if($params['title']) {
            $where['title[~]'] = $params['title'];
        }

        if($params['department']) {
            $where['department'] = $params['department'];
        }

        if($params['start_time']) {
            $where['start_time'] = $params['start_time'];
        }

        if($params['test_channel']) {
            $where['test_channel'] = $params['test_channel'];
        }

        if($params['test_mark']) {
            $where['test_mark'] = $params['test_mark'];
        }

        if($params['test_status']) {
            $where['test_status'] = mAbTestBasicInfo::NORMAL_STATUS;
            if($params['test_status'] == mAbTestBasicInfo::NOT_START_BUSINESS_STATUS) {
                $where['start_time[>]'] = date('Y-m-d H:i:s');
            } else if ($params['test_status'] == mAbTestBasicInfo::STARTING_BUSINESS_STATUS) {
                $where['start_time[<]'] = date('Y-m-d H:i:s');
            } else if ($params['test_status'] == mAbTestBasicInfo::RELEASE_BUSINESS_STATUS) {
                $where['test_status'] = mAbTestBasicInfo::RELEASE_STATUS;
            }
        }

        if($params['admin_id']) {
            $where['admin_id'] = $params['admin_id'];
        }

        return $where;
    }

    public function getStrategyForm(Content $content)
    {
        $form = new Form();
        $form->text('test_unique_mark')->placeholder('输入');
        $form->text('test_mark')->placeholder('输入');
        $form->action('/admin/abTest/getStrategy');
        return $content->row($form);
    }

    public function getStrategy(Request $request)
    {
        $testUniqueMark = $request->input('test_unique_mark');
        $mark   = $request->input('test_mark');

        $result = sGetTestStrategy::getStrategyByAdmin($testUniqueMark, $mark);
        $success = new MessageBag([
            'title' => !is_null($result) ? '命中群组为' . $result : '未命中'
        ]);

        return back()->with(compact('success'))->withInput();
    }

    private function sendMsg($testId, $groupId)
    {
        $testInfo  = mAbTestBasicInfo::getInfoByTestId($testId);
        $groupInfo = mAbTestGroupInfo::get('*', ['group_id' => $groupId]);
        if($testInfo['relevant_people']) {
            $relevantPeople = explode(',', $testInfo['relevant_people']);
            $name = Admin::user()->find(Admin::user()->id)->name;
            $content = <<<EOF
{$name} 在后台把 {$testInfo['title']}关闭了
{$groupInfo['title']} 战胜了其他组别以全量上线
查看数据结果地址: {$testInfo['result_url']}
后台地址: https://frieza.haohaozhu.me/admin/abTest
记得改代码哟
 
 ----->  实验标识为【{$testInfo['test_unique_mark']}】 <-----
EOF;
            DingTalkAlert::sendTextMsg(DingTalkAlert::AB_TEST, $content, false, $relevantPeople);
        }
    }

    public function resetCache(Request $request)
    {
        $testId = $request->input('_key');
        $testInfo = mAbTestBasicInfo::getInfoByTestId($testId);
        $groupInfo = mAbTestGroupInfo::getGroupList($testId);
        cTestInfo::setInfo($testInfo['test_unique_mark'], $testInfo);
        cGroupInfo::setInfo($testInfo['test_unique_mark'], $groupInfo);

        $result = [
            'status' => true,
            'toastr' => [
                'type' => 'success',
                'content' => '重置缓存成功',
                'options' => [
                    'positionClass' => 'toast-top-center'
                ]
            ]
        ];

        return response()->json($result);
    }
}